package com.partsoft.dits.log;

import java.sql.Types;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;

import javax.sql.DataSource;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.ContextStoppedEvent;

import com.partsoft.dits.ConfigurableBean;
import com.partsoft.dits.EdgeDescriptor;
import com.partsoft.dits.dsm.DataSourceFactory;
import com.partsoft.dits.utils.DbUtils;
import com.partsoft.dits.utils.ExecutionCallback;
import com.partsoft.dits.utils.JdbcTemplate;
import com.partsoft.dits.utils.SQLTypeUtils;

import org.springframework.transaction.interceptor.TransactionAttribute;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;


public class ContextReferDataSourceLogManager extends BaseDataSourceLogResolver implements LogResolver, LogManager, ConfigurableBean, ApplicationContextAware, ApplicationListener<ApplicationEvent> {
	
	private DataSourceFactory dataSourceFactory;
	
	private String dataSourceName;
	
	private ApplicationContext ownerContext;
	
	private BaseDataSourceLogAdapter logAdapter;
	
	public ContextReferDataSourceLogManager() {}
	
	public ContextReferDataSourceLogManager(DataSourceFactory dataSourceFactory, String dsName) {
		setDataSourceFactory(dataSourceFactory);
		setDataSourceName(dsName);
	}
	
	private LogManager logManager = new AbstractLogManager() {
		
		public void removeLog(LogEntity logEntity) {
			doRemoveLogImplement(logEntity);
		}
		
		@Override
		protected void doRemoveLog(String dclsName, String did, LogType type, LogLevel level, Date startDate, Date endDate, String queryString) {
			doRemoveLogImplement(dclsName, did, type, level, startDate, endDate, queryString);
		}
	};
	
	public DataSourceFactory getDataSourceFactory() {
		return dataSourceFactory;
	}
	
	protected void assertInit() {
		Assert.isTrue(logAdapter != null, "未初始化上下文。");
	}
	
	protected void doRemoveLogImplement(String dclsName, String did, LogType type, LogLevel level, Date startDate, Date endDate, String queryString) {
		assertInit();
		final StringBuffer exSQL = new StringBuffer();
		final StringBuffer lSQL = new StringBuffer();
		final List<Object> exParams = new LinkedList<Object>();
		final List<Object> lParams = new LinkedList<Object>();
		exSQL.append("DELETE FROM ").append(resolveAttatchmentTableName()).append(" WHERE ").append(resolveAttachmentFKColumn()).append(" IN (");
		exSQL.append("SELECT ").append(resolveLogIDColumn()).append(" FROM ").append(resolveLogTableName());
		lSQL.append("DELETE FROM ").append(resolveLogTableName());

		packagedWhereSQL(getNamespace(), exSQL, exParams, dclsName, did, type, level, queryString, startDate, endDate);
		packagedWhereSQL(getNamespace(), lSQL, lParams, dclsName, did, type, level, queryString, startDate, endDate);
		exSQL.append(")");
		
		DataSource ds = getDataSource();
		final JdbcTemplate jdbcHelper = new JdbcTemplate(ds);
		DbUtils.executeInTransaction(ds, TransactionAttribute.PROPAGATION_REQUIRES_NEW, new ExecutionCallback() {
			public void callback() {
				jdbcHelper.update(exSQL.toString(), exParams.toArray(new Object[0]));
				jdbcHelper.update(lSQL.toString(), lParams.toArray(new Object[0]));
			}
		});
		
	}

	protected void doRemoveLogImplement(LogEntity logEntity) {
		assertInit();
		final String aSQL = String.format("DELETE FROM %s WHERE %s = ?", resolveAttatchmentTableName(), resolveAttachmentFKColumn());
		final Object []aParams = new Object[]{SQLTypeUtils.toSqlParameterValue(logEntity.getId(), Types.VARCHAR)}; 
		DataSource ds = getDataSource();
		final JdbcTemplate jdbcHelper = new JdbcTemplate(ds);
		final String lSQL = String.format("DELETE FROM %s WHERE %s = ?", resolveLogTableName(), resolveLogIDColumn());
		DbUtils.executeInTransaction(ds, TransactionAttribute.PROPAGATION_REQUIRES_NEW, new ExecutionCallback() {
			public void callback() {
				jdbcHelper.update(aSQL, aParams);
				jdbcHelper.update(lSQL, aParams);
			}
		});
	}

	public void setDataSourceFactory(DataSourceFactory dataSourceFactory) {
		this.dataSourceFactory = dataSourceFactory;
	}

	public String getDataSourceName() {
		return dataSourceName;
	}

	public void setDataSourceName(String dataSourceName) {
		this.dataSourceName = dataSourceName;
	}
	
	public ApplicationContext getOwnerContext() {
		return ownerContext;
	}
	
	public void setApplicationContext(ApplicationContext context) throws BeansException {
		this.ownerContext = context;
	}
	
	public void finiContext() {
		synchronized (this) {
			this.setDataSource(null);
			logAdapter = null;
		}
	}

	public void initContext() {
		Assert.notNull(getDataSourceFactory(), "未设置数据源工厂。");
		Assert.hasText(getDataSourceName(), "未设置应用的数据源名称。");
		synchronized (this) {
			if (getDataSource() != null) finiContext();
			DataSource ds = getDataSourceFactory().getDataSource(getDataSourceName());
			setDataSource(ds);
			logAdapter = new BaseDataSourceLogAdapter(ds);
			logAdapter.setNamespace(getNamespace());
		}
	}
	
	@Override
	public boolean isImplemented() {
		Assert.isTrue(logAdapter != null, "未初始化上下文。");
		return logAdapter.isDeployed();
	}
	
	public BaseDataSourceLogAdapter getLogAdapter() {
		Assert.isTrue(logAdapter != null, "未初始化上下文。");
		return logAdapter;
	}

	public void removeLog(LogEntity logEntity) {
		logManager.removeLog(logEntity);
	}

	public void removeLog(EdgeDescriptor descriptor) {
		logManager.removeLog(descriptor);
	}

	public void removeLog(LogLevel level) {
		logManager.removeLog(level);
	}

	public void removeLog(EdgeDescriptor descriptor, LogLevel level) {
		logManager.removeLog(descriptor, level);
	}

	public void removeLog(LogLevel level, String queryString) {
		logManager.removeLog(level, queryString);
	}

	public void removeLog(EdgeDescriptor descriptor, LogLevel level,
			String queryString) {
		logManager.removeLog(descriptor, level, queryString);
	}

	public void removeLog(LogType type) {
		logManager.removeLog(type);
	}

	public void removeLog(EdgeDescriptor descriptor, LogType type) {
		logManager.removeLog(descriptor, type);
	}

	public void removeLog(LogType type, String queryString) {
		logManager.removeLog(type, queryString);
	}

	public void removeLog(EdgeDescriptor descriptor, LogType type,
			String queryString) {
		logManager.removeLog(descriptor, type, queryString);
	}

	public void removeLog(LogType logType, LogLevel level) {
		logManager.removeLog(logType, level);
	}

	public void removeLog(LogType logType, LogLevel level, String queryString) {
		logManager.removeLog(logType, level, queryString);
	}

	public void removeLog(EdgeDescriptor descriptor, LogType logType,
			LogLevel level, String queryString) {
		logManager.removeLog(descriptor, logType, level, queryString);
	}

	public void removeLogBefore(Date beforeDate) {
		logManager.removeLogBefore(beforeDate);
	}

	public void removeLogBefore(EdgeDescriptor descriptor, Date beforeDate) {
		logManager.removeLogBefore(descriptor, beforeDate);
	}

	public void removeLogBefore(Date beforeDate, String queryString) {
		logManager.removeLogBefore(beforeDate, queryString);
	}

	public void removeLogBefore(EdgeDescriptor descriptor, Date beforeDate,
			String queryString) {
		logManager.removeLogBefore(descriptor, beforeDate, queryString);
	}

	public void removeLogBefore(LogType type, Date beforeDate) {
		logManager.removeLogBefore(type, beforeDate);
	}

	public void removeLogBefore(EdgeDescriptor descriptor, LogType type,
			Date beforeDate) {
		logManager.removeLogBefore(descriptor, type, beforeDate);
	}

	public void removeLogBefore(LogType type, Date beforeDate,
			String queryString) {
		logManager.removeLogBefore(type, beforeDate, queryString);
	}

	public void removeLogBefore(EdgeDescriptor descriptor, LogType type,
			Date beforeDate, String queryString) {
		logManager.removeLogBefore(descriptor, type, beforeDate, queryString);
	}

	public void removeLogBefore(LogType type, LogLevel level, Date beforeDate) {
		logManager.removeLogBefore(type, level, beforeDate);
	}

	public void removeLogBefore(EdgeDescriptor descriptor, LogType type,
			LogLevel level, Date beforeDate) {
		logManager.removeLogBefore(descriptor, type, level, beforeDate);
	}

	public void removeLogBefore(LogType type, LogLevel level, Date beforeDate,
			String queryString) {
		logManager.removeLogBefore(type, level, beforeDate, queryString);
	}

	public void removeLogBefore(EdgeDescriptor descriptor, LogType type,
			LogLevel level, Date beforeDate, String queryString) {
		logManager.removeLogBefore(descriptor, type, level, beforeDate,
				queryString);
	}

	public void removeLogBefore(LogLevel level, Date beforeDate) {
		logManager.removeLogBefore(level, beforeDate);
	}

	public void removeLogBefore(EdgeDescriptor descriptor, LogLevel level,
			Date beforeDate) {
		logManager.removeLogBefore(descriptor, level, beforeDate);
	}

	public void removeLogBefore(LogLevel level, Date beforeDate,
			String queryString) {
		logManager.removeLogBefore(level, beforeDate, queryString);
	}

	public void removeLogBefore(EdgeDescriptor descriptor, LogLevel level,
			Date beforeDate, String queryString) {
		logManager.removeLogBefore(descriptor, level, beforeDate, queryString);
	}

	public void removeLogAfter(Date afterDate) {
		logManager.removeLogAfter(afterDate);
	}

	public void removeLogAfter(EdgeDescriptor descriptor, Date afterDate) {
		logManager.removeLogAfter(descriptor, afterDate);
	}

	public void removeLogAfter(Date afterDate, String queryString) {
		logManager.removeLogAfter(afterDate, queryString);
	}

	public void removeLogAfter(EdgeDescriptor descriptor, Date afterDate,
			String queryString) {
		logManager.removeLogAfter(descriptor, afterDate, queryString);
	}

	public void removeLogAfter(LogType type, Date afterDate) {
		logManager.removeLogAfter(type, afterDate);
	}

	public void removeLogAfter(EdgeDescriptor descriptor, LogType type,
			Date afterDate) {
		logManager.removeLogAfter(descriptor, type, afterDate);
	}

	public void removeLogAfter(LogType type, Date afterDate, String queryString) {
		logManager.removeLogAfter(type, afterDate, queryString);
	}

	public void removeLogAfter(EdgeDescriptor descriptor, LogType type,
			Date afterDate, String queryString) {
		logManager.removeLogAfter(descriptor, type, afterDate, queryString);
	}

	public void removeLogAfter(LogLevel level, Date afterDate) {
		logManager.removeLogAfter(level, afterDate);
	}

	public void removeLogAfter(EdgeDescriptor descriptor, LogLevel level,
			Date afterDate) {
		logManager.removeLogAfter(descriptor, level, afterDate);
	}

	public void removeLogAfter(LogLevel level, Date afterDate,
			String queryString) {
		logManager.removeLogAfter(level, afterDate, queryString);
	}

	public void removeLogAfter(EdgeDescriptor descriptor, LogLevel level,
			Date afterDate, String queryString) {
		logManager.removeLogAfter(descriptor, level, afterDate, queryString);
	}

	public void removeLogAfter(LogType type, LogLevel level, Date afterDate) {
		logManager.removeLogAfter(type, level, afterDate);
	}

	public void removeLogAfter(EdgeDescriptor descriptor, LogType type,
			LogLevel level, Date afterDate) {
		logManager.removeLogAfter(descriptor, type, level, afterDate);
	}

	public void removeLogAfter(LogType type, LogLevel level, Date afterDate,
			String queryString) {
		logManager.removeLogAfter(type, level, afterDate, queryString);
	}

	public void removeLogAfter(EdgeDescriptor descriptor, LogType type,
			LogLevel level, Date afterDate, String queryString) {
		logManager.removeLogAfter(descriptor, type, level, afterDate,
				queryString);
	}

	public void removeLogBetween(Date startDate, Date endDate) {
		logManager.removeLogBetween(startDate, endDate);
	}

	public void removeLogBetween(EdgeDescriptor descriptor, Date startDate,
			Date endDate) {
		logManager.removeLogBetween(descriptor, startDate, endDate);
	}

	public void removeLogBetween(Date startDate, Date endDate,
			String queryString) {
		logManager.removeLogBetween(startDate, endDate, queryString);
	}

	public void removeLogBetween(EdgeDescriptor descriptor, Date startDate,
			Date endDate, String queryString) {
		logManager
				.removeLogBetween(descriptor, startDate, endDate, queryString);
	}

	public void removeLogBetween(LogType type, Date startDate, Date endDate) {
		logManager.removeLogBetween(type, startDate, endDate);
	}

	public void removeLogBetween(EdgeDescriptor descriptor, LogType type,
			Date startDate, Date endDate) {
		logManager.removeLogBetween(descriptor, type, startDate, endDate);
	}

	public void removeLogBetween(LogType type, Date startDate, Date endDate,
			String queryString) {
		logManager.removeLogBetween(type, startDate, endDate, queryString);
	}

	public void removeLogBetween(EdgeDescriptor descriptor, LogType type,
			Date startDate, Date endDate, String queryString) {
		logManager.removeLogBetween(descriptor, type, startDate, endDate,
				queryString);
	}

	public void removeLogBetween(LogLevel level, Date startDate, Date endDate) {
		logManager.removeLogBetween(level, startDate, endDate);
	}

	public void removeLogBetween(EdgeDescriptor descriptor, LogLevel level,
			Date startDate, Date endDate) {
		logManager.removeLogBetween(descriptor, level, startDate, endDate);
	}

	public void removeLogBetween(LogLevel level, Date startDate, Date endDate,
			String queryString) {
		logManager.removeLogBetween(level, startDate, endDate, queryString);
	}

	public void removeLogBetween(EdgeDescriptor descriptor, LogLevel level,
			Date startDate, Date endDate, String queryString) {
		logManager.removeLogBetween(descriptor, level, startDate, endDate,
				queryString);
	}

	public void removeLogBetween(LogType type, LogLevel level, Date startDate,
			Date endDate) {
		logManager.removeLogBetween(type, level, startDate, endDate);
	}

	public void removeLogBetween(EdgeDescriptor descriptor, LogType type,
			LogLevel level, Date startDate, Date endDate) {
		logManager
				.removeLogBetween(descriptor, type, level, startDate, endDate);
	}

	public void removeLogBetween(LogType type, LogLevel level, Date startDate,
			Date endDate, String queryString) {
		logManager.removeLogBetween(type, level, startDate, endDate,
				queryString);
	}

	public void removeLogBetween(EdgeDescriptor descriptor, LogType type,
			LogLevel level, Date startDate, Date endDate, String queryString) {
		logManager.removeLogBetween(descriptor, type, level, startDate,
				endDate, queryString);
	}
	
	public void removeLogBetween(String dclsName, String did, LogType type,
			LogLevel level, Date startDate, Date endDate, String queryString) {
		logManager.removeLogBetween(dclsName, did, type, level, startDate, endDate, queryString);
	}
	
	public void removeLogAfter(String dclsName, String did, LogType type, LogLevel level, Date afterDate, String queryString) {
		logManager.removeLogAfter(dclsName, did, type, level, afterDate, queryString);
	}
	
	public void removeLogBefore(String dclsName, String did, LogLevel level, Date beforeDate, String queryString) {
		logManager.removeLogBefore(dclsName, did, level, beforeDate, queryString);
	}
	
	public void removeLog(String dclsName, String did, LogType type, String queryString) {
		logManager.removeLog(dclsName, did, type, queryString);
	}
	
	
	public void removeLog(String dclsName, String did, LogLevel level, String queryString) {
		logManager.removeLog(dclsName, did, level, queryString);
	}
	
	public void removeLog(String dclsName, String did) {
		logManager.removeLog(dclsName, did);
	}

	public void onApplicationEvent(ApplicationEvent event) {
		if (getOwnerContext() == null)
			return;
		if (!ObjectUtils.nullSafeEquals(getOwnerContext(), event.getSource()))
			return;
		if (event instanceof ContextRefreshedEvent) {
			initContext();
		} else if (event instanceof ContextStoppedEvent) {
			finiContext();
		}
	}
}